home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / nn.zip / MENU.C < prev    next >
C/C++ Source or Header  |  1989-12-31  |  31KB  |  1,472 lines

  1. /*
  2.  *     selection mode menu
  3.  */
  4.  
  5. #include "config.h"
  6. #include "articles.h"
  7. #include "term.h"
  8. #include "keymap.h"
  9. #include "menu.h"
  10. #include "regexp.h"
  11.  
  12.  
  13. export int  preview_window = 0;    /* size of preview window */
  14. export int  fmt_linenum    = 1; /* menu line format */
  15. export int  fmt_rptsubj    = 0; /* repeat identical subjects if !0 */
  16. export int  novice       = 1; /* novice mode -- use extended prompts */
  17. export int  long_menu       = 0; /* don't put empty lines around menu lines */
  18. export int  delay_redraw   = 0; /* prompt again if :-command clears screen */
  19. export int  slow_mode       = 0;    /* mark selected articles with *s */
  20. export int  re_layout      = 0; /* Re: format presentation on menus */
  21. export int  collapse_subject = 25; /* collapse long subjects at position */
  22. export int  conf_group_entry  = 0; /* ask whether group should be entered */
  23. export int  auto_preview_mode = 0; /* preview rather than select */
  24.  
  25. export char *delayed_msg = NULL;    /* give to msg() after redraw */
  26. export long dl_msg_arg = 0;        /* optional arg to delayed_msg */
  27.  
  28. import also_read_articles;
  29. import merged_menu;
  30.  
  31. #ifdef NNTP
  32. import int use_nntp;            /* is nntp in use? */
  33. #endif
  34.  
  35. extern group_completion();
  36.  
  37. static regexp *regular_expr = NULL;
  38.  
  39. static int firstl;    /* first menu line */
  40.  
  41. static article_number firsta;    /* first article on menu (0 based) */
  42. static int cura;    /* current article */
  43. static int next_cura;    /* article to become cura if >= 0 */
  44. static int numa;    /* no of articles on menu - 1 */
  45.  
  46. #define INTERVAL1    ('z' - 'a' + 1)
  47. #define INTERVAL2    ('9' - '0' + 1)
  48.  
  49. char ident[] = "abcdefghijklmnopqrstuvwxyz0123456789";
  50. char Ident[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ&&&&&&&&&&";
  51.  
  52. /* mark commands */
  53.  
  54. #define    OFF    0
  55. #define    ON    1
  56. #define    TOGGLE    2
  57. #define    INIT    3
  58. #define    SAVED    4
  59. #define    REMOVE    5
  60. #define CANCEL    6
  61. #define    ON_LEAVE 7
  62. #define ON_READ 8
  63.  
  64. static int how;
  65.  
  66. static mark()
  67. {
  68.     register article_header *ah;
  69.     int lno, curhow, must_print, lnum, lsubj, lname;
  70.  
  71.     ah = articles[firsta + cura];
  72.     curhow = (ah->flag & A_SELECT) ? ON : OFF;
  73.     if (how == curhow) return;
  74.  
  75.     lno = firstl + cura;
  76.     must_print = !slow_mode && STANDOUT;
  77.  
  78.  toggle:
  79.  
  80.     switch (how) {
  81.   
  82.      case TOGGLE:
  83.     how = (curhow == ON) ? OFF : ON;
  84.     goto toggle;
  85.     
  86.      case ON:
  87.     if (ah->flag & A_CANCEL) {
  88.         if (curhow == OFF) return;
  89.         how = OFF;
  90.         goto toggle;
  91.     }
  92.     ah->flag &= ~(A_LEAVE | A_READ);
  93.     ah->flag |= A_SELECT;
  94.     break;
  95.     
  96.      case OFF:
  97.     ah->flag &= ~(A_SELECT | A_AUTO);
  98.     break;
  99.  
  100.      case INIT:
  101.     gotoxy(0, lno);
  102.     putchar(ident[cura]);
  103.     how = curhow;
  104.     must_print = 1;
  105.     break;
  106.  
  107.      case SAVED:
  108.     gotoxy(0, lno);
  109.     putchar(Ident[cura]);
  110.     return;
  111.  
  112.      case REMOVE:
  113.     gotoxy(0, lno);
  114.     clrline();
  115.     return;
  116.  
  117.      case CANCEL:
  118.     if (must_print && (ah->flag & A_SELECT)) {
  119.         ah->flag &= ~(A_SELECT | A_AUTO);
  120.         break;
  121.     }
  122.     ah->flag &= ~(A_SELECT | A_AUTO | A_LEAVE | A_LEAVE_NEXT | A_READ);
  123.     gotoxy(1, lno);
  124.     putchar((ah->flag & A_CANCEL) ? '#' : ' ');
  125.     return;
  126.  
  127.      case ON_READ:
  128.     if (cura < 0 || cura > numa) return;
  129.     gotoxy(1, lno);
  130.     putchar((ah->flag & A_READ) ? '.' : ' ');
  131.     return;
  132.  
  133.      case ON_LEAVE:
  134.     if (cura < 0 || cura > numa) return;
  135.     gotoxy(1, lno);
  136.     putchar((ah->flag & A_LEAVE) ? '+' :
  137.         (ah->flag & A_LEAVE_NEXT) ? '=' : ' ');
  138.     return;
  139.     
  140.     }
  141.  
  142.     if (cura < 0 || cura > numa) return;
  143.  
  144.     /* menu line formats:
  145.                 1  3    8 10     20 22        xx
  146.         :  :    :  :      :  :        :
  147.        0    id name           subject     +lines
  148.        1    id name       lines  subject
  149.        2    id  lines  subject
  150.        3    id subject
  151.        4    id name:8 subject
  152.      */
  153.  
  154.     if (must_print) {
  155.  
  156.     if (fmt_linenum > 5) fmt_linenum = 1;
  157.     
  158.     if (ah->flag & (A_CANCEL | A_LEAVE | A_LEAVE_NEXT | A_READ)) {
  159.         gotoxy(1, lno);
  160.         putchar(
  161.             (ah->flag & A_READ) ? '.' :
  162.             (ah->flag & A_LEAVE) ? '+' :
  163.             (ah->flag & A_CANCEL) ? '#' :
  164.             '='
  165.             );
  166.     } else
  167.     if (how == ON && !slow_mode) {
  168.         if (so_gotoxy(1, lno, 1) == 0) putchar('*');
  169.     } else {
  170.         gotoxy(1, lno);
  171.         putchar(how == ON ? '*' : ' ');
  172.     }
  173.  
  174.     if (ah->lines <    10) lnum = 1; else
  175.     if (ah->lines <   100) lnum = 2; else
  176.     if (ah->lines <  1000) lnum = 3; else
  177.     if (ah->lines < 10000) lnum = 4; else lnum = 5;
  178.     
  179.     lsubj = Columns - cookie_size - 2; /* ident char + space */
  180.  
  181.     switch (fmt_linenum) {
  182.         
  183.      case 0:
  184.         lsubj -= NAME_LENGTH + 1 + 2 + lnum;  /* name. .subj. +.lines */
  185.         so_printf("%-*s ", NAME_LENGTH, ah->sender);
  186.         break;
  187.  
  188.      case 5:
  189.         if (ah->subj_length > (lsubj - NAME_LENGTH - 5))
  190.         if (fmt_rptsubj || lno == firstl || (ah->flag & A_SAME) == 0) {
  191.             so_printf("  ");
  192.             lsubj -= 2;
  193.             break;
  194.         }
  195.         /* else use layout 1, so fall thru */
  196.  
  197.      case 1:
  198.         lsubj -= NAME_LENGTH + 5;
  199.         /* name.lines.  .subj (name may be shortened) */
  200.         lname = NAME_LENGTH + 2 - lnum;
  201.         so_printf("%-*.*s ", lname, lname, ah->sender);
  202.         so_printf(ah->lines >= 0 ? "%d  " : "?  ", ah->lines);
  203.         break;
  204.        
  205.      case 2:
  206.         lsubj -= 6;
  207.         so_printf("%5d ", ah->lines);
  208.         break;
  209.         
  210.      case 3:
  211.         break;
  212.  
  213.      case 4:
  214.         lsubj -= 9;
  215.         so_printf("%-8.8s ", ah->sender);
  216.         break;
  217.     }
  218.  
  219.     if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
  220.         if (ah->replies == 0 || prt_replies(ah->replies) == 0)
  221.         so_printf("-");
  222.     } else {
  223.         lsubj -= prt_replies(ah->replies);
  224.         if (lsubj >= ah->subj_length)
  225.         so_printf("%s", ah->subject);
  226.         else
  227.         if (collapse_subject < 0)
  228.         so_printf("%-.*s", lsubj, ah->subject);
  229.         else {
  230.         if (collapse_subject > 0)
  231.             so_printf("%-.*s", collapse_subject, ah->subject);
  232.         lsubj -= 2 + collapse_subject;
  233.         so_printf("<>%s", ah->subject + ah->subj_length - lsubj);
  234.         }        
  235.     }    
  236.  
  237.     if (fmt_linenum == 0)
  238.         so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  239.  
  240.     so_end();
  241.     } else {
  242.     gotoxy(1, lno);
  243.     putchar((how == OFF) ? ' ' : '*');
  244.     }    
  245.     
  246.     return;
  247. }
  248.  
  249.  
  250. static prt_replies(level)
  251. {
  252.     int re;
  253.     
  254.     if (level == 0) return 0;
  255.     re = level & 0x80;
  256.     level &= 0x7f;
  257.  
  258.     switch (re_layout) {
  259.      case 1:
  260.     if (!re) return 0;
  261.     so_printf(">");
  262.     return 1;
  263.      case 2:
  264.     so_printf("%d>", level);
  265.     return level < 10 ? 2 : 3;
  266.      case 3:
  267.     so_printf("Re: ");
  268.     return 4;
  269.     }
  270.     
  271.     if (level < 10) {
  272.     so_printf("%-.*s", level, ">>>>>>>>>");
  273.     return level;
  274.     }
  275.     
  276.     so_printf(">>>%3d >>>>", level);
  277.     return 11;
  278. }
  279.  
  280.  
  281. static int article_id;
  282.  
  283. static int get_k_cmd()
  284. {
  285.     register int c, map;
  286.  
  287.  loop:
  288.     
  289.     article_id = -1;
  290.     
  291.     if ((c = get_c()) & GETC_COMMAND)
  292.     map = c & ~GETC_COMMAND;
  293.     else
  294.     map = menu_key_map[c];
  295.     
  296.     if (s_hangup) map = K_QUIT;
  297.     
  298.     if (map & K_MACRO) {
  299.     m_invoke(map & ~K_MACRO);
  300.     goto loop;
  301.     }
  302.     
  303.     if (map & K_ARTICLE_ID) {
  304.     article_id = map & ~K_ARTICLE_ID;
  305.     map = K_ARTICLE_ID;
  306.     
  307.     if (article_id < 0 || article_id > numa) {
  308.         ding();
  309.         goto loop;
  310.     }
  311.     }    
  312.     
  313.     return map;
  314. }
  315.  
  316.  
  317. char *pct(start, end, first, last)
  318. long start, end, first, last;
  319. {
  320.     long n = end - start;
  321.     static char buf[16];
  322.     char *fmt;
  323.     
  324.     if (first <= start || n <= 0)
  325.     if (last >= end || n <= 0) 
  326.         return "All";
  327.     else
  328.         fmt = "Top %d%%";
  329.     else
  330.     if (last >= end)
  331.         return "Bot";
  332.     else
  333.         fmt = "%d%%";
  334.     
  335.     sprintf(buf, fmt, ((last - start) * 100)/n);
  336.     return buf;
  337. }
  338.  
  339. menu(print_header)
  340. int (* print_header)();
  341. {
  342.     register         k_cmd, cur_k_cmd;
  343.     register        article_header *ah;
  344.     int            last_k_cmd, last_how;
  345.     int         seen_all, menu_cmd, temp;
  346.     int         save_selected;
  347.     article_number    last_save;
  348.     int            doing_unshar, did_unshar;
  349.     char         *fname, *savemode, *init_save();
  350.     article_number    nexta;    /* first article on next menu */
  351.     int         maxa;    /* max no of articles per menu page */
  352.     int         o_firsta, o_mode;    /* for recursive calls */
  353.     static        menu_level = 0;
  354.     char        purpose[80], pr_fmt[60];
  355.     extern int         enable_stop, file_completion();
  356.     extern int        alt_cmd_key, in_menu_mode, any_message;
  357.     article_number    elim_list[3];
  358.     int            entry_check = conf_group_entry;
  359.     
  360. #define    menu_return(cmd) \
  361.     { menu_cmd = (cmd); goto menu_exit; }
  362.  
  363.     flush_input();
  364.     
  365.     o_firsta = firsta;
  366.     o_mode = in_menu_mode;
  367.     in_menu_mode = 1;
  368.     
  369.     menu_level++;
  370.  
  371.     sprintf(pr_fmt, 
  372.         menu_level == 1 ? 
  373.           "\1\2-- SELECT %s-----%%s-----\1" :
  374.           "\1\2-- SELECT %s-----%%s-----<%s%d>--\1",
  375.           novice ? "-- help:? " : "",
  376.           novice ? "level " : "",
  377.           menu_level);
  378.  
  379.     purpose[0] = NUL;
  380.     if (!merged_menu && current_group->group_flag & G_NEW)
  381.     get_purpose(purpose);
  382.     
  383.     seen_all = 0;
  384.     firsta = 0;
  385.     while (firsta < n_articles && articles[firsta]->flag & A_SEEN)
  386.     firsta++;
  387.  
  388.     if (firsta == n_articles) {
  389.     seen_all++;
  390.     firsta = 0;
  391.     }
  392.     
  393.     next_cura = -1;
  394.     cur_k_cmd = K_UNBOUND;
  395.     
  396. #ifdef HAVE_JOBCONTROL
  397. #define    REDRAW_CHECK    if (s_redraw) goto do_redraw
  398.     
  399.  do_redraw:
  400.     /* safe to clear here, because we are going to redraw anyway */
  401.     s_redraw = 0; 
  402. #else
  403. #define REDRAW_CHECK
  404. #endif
  405.  
  406.  redraw:
  407.     s_keyboard = 0;
  408.  
  409.  empty_menu_hack:    /* do: "s_keyboard=1; goto empty_menu_hack;" */
  410.     if (!slow_mode) s_keyboard = 0;
  411.     
  412.     nexta = firsta;
  413.     
  414.     clrdisp();
  415.     
  416.     firstl = (*print_header)();
  417.     maxa = Lines - preview_window - firstl - 2;
  418.     if (!long_menu) firstl++, maxa -= 2;
  419.  
  420.     if (maxa > (INTERVAL1 + INTERVAL2))
  421.     maxa = INTERVAL1 + INTERVAL2;
  422.  
  423.  nextmenu:
  424.  
  425.     no_raw();
  426.     gotoxy(0, firstl);
  427.     clrpage(firstl);
  428.     
  429.     if (nexta > 0) {
  430.     while (firsta < nexta)
  431.         articles[firsta++]->flag |= A_SEEN;
  432.     } else
  433.     if (purpose[0]) {
  434.         msg(purpose);
  435.     }
  436.  
  437.     firsta = nexta;
  438.     numa = Lines; /* for mark; is set correctly below */
  439.     cura = 0;
  440.     
  441.     REDRAW_CHECK;
  442.     
  443.     if (!s_keyboard)
  444.     while (nexta < n_articles && cura < maxa) {
  445.         
  446.         REDRAW_CHECK;
  447.     
  448.         if (entry_check && menu_level == 1 && 
  449.         (current_group->group_flag & G_READ) == 0) {
  450.         entry_check = 0;
  451.         prompt_line = firstl;
  452.         prompt("\1Enter?\1 ");
  453.         if ((temp = yes(0)) == 0) menu_return(ME_NEXT);
  454.         if (temp < 0) {
  455.             prompt("\1Mark as read?\1 ");
  456.             if ((temp = yes(0)) > 0) menu_return(ME_READ);
  457.             if (temp < 0) menu_return(ME_QUIT);
  458.             menu_return(ME_NEXT);
  459.         }
  460.         
  461.         gotoxy(0, firstl);
  462.         clrline();
  463.         }
  464.         
  465.         how = INIT;
  466.         mark(); /* fl; */
  467.         
  468.         if (s_keyboard) {      /* Signal may have corrupted output.  */
  469.         if (cura == 0)
  470.             mark();      /* redraw first entry */
  471.         else {
  472.             how = REMOVE; /* We delete the last entry (the user */
  473.             mark();      /* wanted to stop the output anyway)  */
  474.             cura--;
  475.             nexta--;
  476.         }            
  477.         break;
  478.         }
  479.         
  480.         nexta++; cura++;
  481.     }
  482.  
  483.     fl;
  484.     s_keyboard = 0;
  485.  
  486.     prompt_line = firstl + cura;
  487.     if (!long_menu || cura < maxa) prompt_line++;
  488.  
  489.     numa = nexta - firsta - 1;
  490.      if (numa < 0) prompt_line++;
  491.  
  492.      if (next_cura >= 0) {
  493.      cura = next_cura;
  494.      next_cura = -1;
  495.      } else {
  496.      cura = 0;
  497.      for (article_id = firsta; cura < numa; article_id++, cura++)
  498.          if ((articles[article_id]->flag & A_SELECT) == 0) break;
  499.      }
  500.  
  501.      how = TOGGLE;
  502.  
  503.   build_prompt:
  504.  
  505.      raw();
  506.  
  507.   Prompt:
  508.  
  509.      prompt(pr_fmt,
  510.         pct(0L, (long)(n_articles-1), (long)firsta, (long)(firsta+numa)));
  511.  
  512.      if (delayed_msg != NULL) {
  513.      msg(delayed_msg, dl_msg_arg);
  514.      delayed_msg = NULL;
  515.      }
  516.  
  517.   same_prompt:
  518.  
  519.      if (cura < 0 || cura > numa) cura = 0;
  520.  
  521.      if (numa >= 0) {
  522.      gotoxy(0, firstl + cura); 
  523.      fl; /* place cursor at current article id */
  524.      save_xy();
  525.      }
  526.  
  527.      last_k_cmd = cur_k_cmd;
  528.      k_cmd = get_k_cmd();
  529.      if (any_message) clrmsg(-1);
  530.  
  531.   alt_key:
  532.  
  533.      switch (cur_k_cmd = k_cmd) {
  534.  
  535.       case K_UNBOUND:
  536.      ding();
  537.      flush_input();
  538.       case K_INVALID:
  539.      goto same_prompt;
  540.  
  541.       case K_REDRAW:
  542.      next_cura = cura;
  543.      goto redraw;
  544.  
  545.       case K_LAST_MESSAGE:
  546.      msg((char *)NULL);
  547.      goto same_prompt;
  548.  
  549.       case K_HELP:
  550.      if (numa < 0)  goto nextmenu;    /* give specific help here */
  551.      display_help("menu");
  552.      goto redraw;
  553.  
  554.       case K_SHELL:
  555.      if (group_file_name) *group_file_name = NUL;
  556.      if (shell_escape()) goto redraw;
  557.      goto Prompt;
  558.  
  559.       case K_VERSION:
  560.      prompt(P_VERSION);
  561.      goto same_prompt;
  562.  
  563.       case K_EXTENDED_CMD:
  564.      switch (alt_command()) {
  565.  
  566.       case AC_UNCHANGED:
  567.          goto same_prompt;
  568.          
  569.       case AC_QUIT:
  570.          menu_return( ME_QUIT );
  571.  
  572.       case AC_PROMPT:
  573.          goto Prompt;
  574.  
  575.       case AC_REORDER:
  576.          firsta = 0;
  577.          /* fall thru */
  578.       case AC_REDRAW:
  579.          goto redraw;
  580.  
  581.       case AC_KEYCMD:
  582.          k_cmd = alt_cmd_key;
  583.          goto alt_key;
  584.  
  585.       case AC_HEADER:
  586.          home();
  587.          (*print_header)();
  588.          goto Prompt;
  589.      }
  590.  
  591.       case K_QUIT:
  592.      menu_return(ME_QUIT);
  593.  
  594.       case K_CANCEL:
  595.      savemode = "Cancel";
  596.      fname = "";
  597.      goto cancel1;
  598.      
  599.       case K_SAVE_NO_HEADER:
  600.       case K_SAVE_SHORT_HEADER:
  601.       case K_SAVE_FULL_HEADER:
  602.       case K_PRINT:
  603.       case K_UNSHAR:
  604.       case K_PATCH:
  605.       case K_UUDECODE:
  606.      
  607.      if (numa < 0) goto nextmenu;
  608.  
  609.      fname = init_save(k_cmd, &savemode);
  610.      if (fname == NULL) goto Prompt;
  611.  
  612.       cancel1:
  613.      enable_stop = 0;
  614.      save_selected = 0;
  615.      doing_unshar = k_cmd == K_UNSHAR || k_cmd == K_PATCH;
  616.      did_unshar = 0;
  617.  
  618.      m_startinput();
  619.  
  620.      if (novice) 
  621.          msg(" * selected articles on this page, + all selected articles");
  622.      
  623.      while (!save_selected && !did_unshar) {
  624.          prompt("\1%s\1 %.*s Article (* +): ",
  625.             savemode, Columns - 25, fname);
  626.  
  627.          k_cmd = get_k_cmd();
  628.  
  629.          if (k_cmd == K_SELECT_SUBJECT) {
  630.          save_selected = 1;
  631.          cura = 0;
  632.          article_id = firsta;
  633.          last_save = firsta + numa;
  634.          } else
  635.          if (k_cmd == K_AUTO_SELECT) {
  636.          save_selected = 2;
  637.          cura = -firsta;
  638.          article_id = 0;
  639.          last_save = n_articles - 1;
  640.          } else
  641.          if (k_cmd == K_ARTICLE_ID) {
  642.          cura = article_id;
  643.          article_id += firsta;
  644.          last_save = article_id;
  645.          } else
  646.          break;
  647.  
  648.          for ( ; article_id <= last_save ; article_id++, cura++) {
  649.          ah = articles[article_id];
  650.          if (save_selected && (ah->flag & A_SELECT) == 0) continue;
  651.  
  652.          if (cur_k_cmd == K_CANCEL) {
  653.              if (current_group->group_flag & G_FOLDER) {
  654.              if ((ah->flag & A_CANCEL) == 0) fcancel(ah);
  655.              } else
  656.              switch (cancel(ah)) {
  657.               case -1:
  658.                  did_unshar = 1;
  659.                  continue;
  660.               case 0:
  661.                  ah->flag |= A_CANCEL;
  662.                  break;
  663.               default:
  664.                  continue;
  665.              }
  666.              
  667.              if (!did_unshar) {
  668.              how = CANCEL;
  669.              mark();
  670.              }
  671.              continue;
  672.          }
  673.  
  674.          if (doing_unshar) {
  675.              did_unshar++;
  676.          } else
  677.          if (cura >= 0 && cura <= numa)
  678.              prompt("Processing %c...", ident[cura]);
  679.          else if (ah->subject != NULL)
  680.              prompt("Processing '%.50s'...", ah->subject);
  681.          else
  682.              prompt("Processing entry %d...", article_id);
  683.  
  684.          if (save(ah)) {
  685.              if (doing_unshar) {
  686.              if (save_selected)
  687.                  ah->flag &= ~(A_SELECT | A_AUTO);
  688.              } else
  689.              if (cura >= 0 && cura <= numa) {
  690.              how = save_selected ? OFF : SAVED;
  691.              mark();
  692.              how = TOGGLE;
  693.              } else
  694.              if (save_selected)
  695.              ah->flag &= ~(A_SELECT | A_AUTO);
  696.          }
  697.          }
  698.      }
  699.  
  700.      if (save_selected) cura = 0;
  701.      how = TOGGLE;
  702.  
  703.      m_endinput();
  704.  
  705.      enable_stop = 1;
  706.      if (cur_k_cmd != K_CANCEL)
  707.          end_save();
  708.  
  709.      if (did_unshar) {
  710.          printf("\r\n");
  711.          any_key(0);
  712.          goto redraw;
  713.      }    
  714.      goto Prompt;
  715.  
  716.       case K_FOLLOW_UP:
  717. #ifdef NNTP_POST
  718.      if (use_nntp && nntp_no_post()) goto same_prompt;
  719. #endif
  720.       case K_REPLY:
  721.      if (numa < 0) goto nextmenu;
  722.  
  723.      prompt(k_cmd == K_REPLY ? 
  724.         "\1Reply to author\1 of article: " : 
  725.         "\1Follow Up\1 to article: ");
  726.  
  727.      if (get_k_cmd() == K_ARTICLE_ID)
  728.          if (answer(articles[firsta+article_id], k_cmd, -1))
  729.          goto redraw;
  730.  
  731.      goto Prompt;
  732.  
  733.       case K_POST:
  734.  
  735. #ifdef NNTP_POST
  736.      if (use_nntp && nntp_no_post())
  737.          goto same_prompt;
  738. #endif
  739.      if (post_menu()) goto redraw;
  740.      goto Prompt;
  741.  
  742.       case K_MAIL_OR_FORWARD:
  743.      if (numa < 0) goto nextmenu;
  744.  
  745.      prompt("\1Article to be forwarded\1 (SP if none): ");
  746.  
  747.      if ((k_cmd = get_k_cmd()) == K_ARTICLE_ID) {
  748.          if (answer(articles[firsta+article_id], K_MAIL_OR_FORWARD, 1))
  749.          goto redraw;
  750.      } else
  751.      if (k_cmd == K_CONTINUE)
  752.          if (answer((article_header *)NULL, K_MAIL_OR_FORWARD, 0))
  753.          goto redraw;
  754.  
  755.      goto Prompt;
  756. /*
  757.       case K_CANCEL:
  758.      if (numa < 0) goto nextmenu;
  759.  
  760.      if (current_group->group_flag & G_FOLDER) {
  761.          prompt("\1Cancel Folder\1 Article: ");
  762.          if (get_k_cmd() == K_ARTICLE_ID) {
  763.          cura = article_id;
  764.          fcancel(articles[firsta+article_id]);
  765.          how = CANCEL;
  766.          mark();
  767.          }
  768.          goto Prompt;
  769.      }
  770.  
  771.      prompt("\1Cancel\1 Article: ");
  772.  
  773.      if (get_k_cmd() == K_ARTICLE_ID)
  774.          if (cancel(articles[firsta+article_id]) & 1) goto redraw;
  775.      goto Prompt;
  776. */
  777.       case K_UNSUBSCRIBE:
  778.      if (unsubscribe(current_group)) {
  779.          if (!(current_group->group_flag & G_SUBSCRIPTION))
  780.          menu_return(ME_NEXT);
  781.          home();
  782.          (*print_header)();
  783.      }
  784.      goto Prompt;
  785.  
  786.       case K_GROUP_OVERVIEW:
  787.      group_overview(-1);
  788.      goto redraw;
  789.  
  790.       case K_KILL_HANDLING:
  791.      switch (kill_menu((article_header *)NULL)) {
  792.       case 0:        /* select */
  793.          do_auto_select((regexp *)NULL, 2);
  794.          break;
  795.       case 1:        /* kill */
  796.          if (!do_auto_kill()) break;
  797.          elim_list[0] = firsta;
  798.          elim_list[1] = firsta + cura;
  799.          elim_list[2] = nexta;
  800.          if (elim_articles(elim_list, 3)) {
  801.          firsta = elim_list[0];
  802.          goto redraw;
  803.          }
  804.          firsta = elim_list[0];
  805.          cura   = elim_list[1] - firsta;
  806.          nexta  = elim_list[2];
  807.          break;
  808.       default:
  809.          break;
  810.      }
  811.      goto Prompt;
  812.  
  813.       case K_CONTINUE:    /* goto next menu page or show the articles */
  814.      if (nexta < n_articles && !seen_all) goto nextmenu;
  815.      /* fall thru */
  816.  
  817.       case K_READ_GROUP_UPDATE:
  818.       case K_READ_GROUP_THEN_SAME:
  819.      no_raw();
  820.      clrdisp();
  821.  
  822.      switch (show_articles()) {
  823.  
  824.       case SH_MENU:
  825.          goto redraw;
  826.  
  827.       case SH_QUIT:
  828.          menu_return(ME_QUIT);
  829.  
  830.       case SH_NO_SELECT:
  831.          break;
  832.  
  833.       case SH_NEXT:
  834.          menu_return(ME_NEXT);
  835.  
  836.       case SH_READ:
  837.          if (k_cmd == K_READ_GROUP_THEN_SAME || also_read_articles) goto redraw;
  838.          break;
  839.  
  840.       default:
  841.          user_error("show_articles returned improper value");
  842.      }
  843.  
  844.      menu_return(ME_READ);
  845.  
  846.       case K_NEXT_GROUP_NO_UPDATE:
  847.      menu_return(ME_NEXT);
  848.  
  849.       case K_PREVIOUS:
  850.      menu_return(ME_PREV);
  851.  
  852.       case K_ADVANCE_GROUP:
  853.       case K_BACK_GROUP:
  854.      if (merged_menu) {
  855.          msg("No possible on merged menu");
  856.          goto same_prompt;
  857.      }
  858.      /* FALL THRU */
  859.  
  860.       case K_GOTO_GROUP:
  861.  
  862.      switch (goto_group(k_cmd, (article_header *)NULL)) {
  863.  
  864.       case ME_REDRAW:
  865.          firsta = 0;
  866.          goto redraw;
  867.  
  868.       case ME_NO_ARTICLES:
  869.          msg("No selections made.");
  870.  
  871.       case ME_NO_REDRAW:
  872.          goto Prompt;
  873.  
  874.       case ME_QUIT:
  875.          menu_return( ME_QUIT );
  876.  
  877.       case ME_PREV:
  878.          goto redraw;
  879.  
  880.       case ME_NEXT:
  881.       case ME_READ:
  882.          s_keyboard = 1;
  883.          goto empty_menu_hack;
  884.      }
  885.  
  886.       case K_ARTICLE_ID:
  887.      if (numa < 0) goto nextmenu;
  888.  
  889.      if (auto_preview_mode) goto auto_preview;
  890.      
  891.      cura = article_id; 
  892.      how = TOGGLE;
  893.      mark();
  894.      last_how = how;
  895.      cura++;
  896.  
  897.      goto same_prompt;
  898.  
  899.       case K_SELECT_INVERT:
  900.      if (numa < 0) goto nextmenu;
  901.  
  902.      temp = cura;
  903.  
  904.      no_raw();    /* for x-on/x-off */
  905.      for (cura = 0; cura <= numa; cura++) {
  906.          how = TOGGLE;
  907.          mark();
  908.      }
  909.      fl;
  910.  
  911.      REDRAW_CHECK;
  912.      raw();
  913.  
  914.      how = TOGGLE; cura = temp;
  915.      goto same_prompt;
  916.  
  917.  
  918.       case K_SELECT:
  919.      if (numa < 0) goto nextmenu;
  920.  
  921.      how = TOGGLE;
  922.      mark();
  923.      last_how = how;
  924.      cura++;
  925.      goto same_prompt;
  926.  
  927.       case K_UNSELECT_ALL:
  928.      for (cura = -firsta; cura < n_articles - firsta; cura++) {
  929.          if (last_k_cmd == K_UNSELECT_ALL) {
  930.          if ((articles[firsta + cura]->flag & (A_LEAVE | A_LEAVE_NEXT)) == 0)
  931.              continue;
  932.          articles[firsta + cura]->flag &= ~(A_LEAVE | A_LEAVE_NEXT);
  933.          how = ON_LEAVE;
  934.          } else
  935.          how = OFF;
  936.          mark();
  937.      }
  938.      fl;
  939.      cura = 0;
  940.      goto same_prompt;
  941.  
  942.       case K_NEXT_LINE:
  943.      if (numa < 0) goto nextmenu;
  944.  
  945.      cura++;
  946.      how = TOGGLE;
  947.      goto same_prompt;
  948.  
  949.       case K_PREV_LINE:
  950.      if (numa < 0) goto nextmenu;
  951.  
  952.      if (--cura < 0) cura = numa;
  953.      how = TOGGLE;
  954.      goto same_prompt;
  955.  
  956.       case K_SELECT_SUBJECT:
  957.      if (numa < 0) goto nextmenu;
  958.  
  959.      mark();
  960.  
  961.      while (firsta+cura > 0 && articles[firsta+cura]->flag & A_SAME) 
  962.          cura--;
  963.  
  964.      do {
  965.          mark();
  966.          cura++;
  967.          if (firsta+cura >= n_articles) break;
  968.      } while (articles[firsta+cura]->flag & A_SAME);
  969.  
  970.      how = TOGGLE;
  971.      goto same_prompt;
  972.  
  973.       case K_SELECT_RANGE:
  974.      if (numa < 0) goto nextmenu;
  975.  
  976.      if (last_k_cmd == K_ARTICLE_ID || last_k_cmd == K_SELECT) {
  977.          how = last_how;
  978.          cura--;
  979.          if (cura < 0) cura = numa;
  980.      } else
  981.          how = articles[firsta+cura]->flag & A_SELECT ? OFF : ON;
  982.  
  983.       range_again:
  984.  
  985.      prompt("\1%select range\1 %c-", how == ON ? "S" : "Des", ident[cura]);
  986.  
  987.      k_cmd = get_k_cmd();
  988.      if (k_cmd == K_SELECT_RANGE) {
  989.          how = !how;
  990.          goto range_again;
  991.      }
  992.  
  993.      if (k_cmd != K_ARTICLE_ID) goto Prompt;
  994.  
  995.      if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT) 
  996.          mark();
  997.  
  998.      if (article_id <= cura) {
  999.          while (--cura >= article_id) mark();
  1000.          if (cura < 0) cura = 0;
  1001.      } else {
  1002.          while (++cura <= article_id) mark();
  1003.          if (cura > numa) cura = numa;
  1004.      }
  1005.  
  1006.      how = TOGGLE;
  1007.      goto Prompt;
  1008.  
  1009.       case K_AUTO_SELECT:
  1010.      do_auto_select((regexp *)NULL, 1);
  1011.      goto same_prompt;
  1012.  
  1013.      case K_GOTO_MATCH:
  1014.      prompt("\1Select regexp\1 ");
  1015.      if ((fname = get_s(NONE, NONE, NONE, NO_COMPLETION)) == NULL) 
  1016.          goto Prompt;
  1017.  
  1018.      if (*fname != NUL) {
  1019.          if (regular_expr) free((char *)regular_expr);
  1020.          regular_expr = regcomp(fname);
  1021.      }
  1022.  
  1023.      if (regular_expr == NULL)
  1024.         msg("No previous expression");
  1025.      else
  1026.         do_auto_select(regular_expr, 2);
  1027.  
  1028.      goto Prompt;
  1029.     
  1030.       case K_NEXT_PAGE:
  1031.      if (nexta < n_articles) goto nextmenu;
  1032.      if (firsta == 0) goto same_prompt;
  1033.  
  1034.      seen_all++;
  1035.      nexta = 0;
  1036.      goto nextmenu;
  1037.  
  1038.       case K_PREV_PAGE:
  1039.      if (firsta == 0 && nexta == n_articles) goto same_prompt;
  1040.  
  1041.      nexta = (firsta > 0 ? firsta : n_articles) - maxa;
  1042.      if (nexta <= 1) nexta = 0;
  1043.      goto nextmenu;
  1044.  
  1045.       case K_FIRST_PAGE:
  1046.      if (firsta == 0) goto same_prompt;
  1047.  
  1048.      nexta = 0;
  1049.      goto nextmenu;
  1050.  
  1051.       case K_LAST_PAGE:
  1052.      if (nexta == n_articles) goto same_prompt;
  1053.  
  1054.      nexta = n_articles - maxa;
  1055.      if (nexta <= 1) nexta = 0;
  1056.      goto nextmenu;
  1057.  
  1058.       case K_PREVIEW:
  1059.      if (numa < 0) goto nextmenu;
  1060.  
  1061.       preview_other:
  1062.  
  1063.      prompt("\1Preview article\1");
  1064.      k_cmd = get_k_cmd();
  1065.  
  1066.      if (k_cmd != K_ARTICLE_ID) {
  1067.          if (k_cmd != K_PREVIEW)
  1068.          goto Prompt;
  1069.          article_id = cura;
  1070.      }
  1071.  
  1072.       auto_preview:
  1073.      temp = prompt_line;
  1074.  
  1075.       preview_next:
  1076.      cura = article_id;
  1077.      ah = articles[firsta+cura];
  1078.      
  1079.      no_raw();
  1080.      menu_cmd = more(ah, MM_PREVIEW, prompt_line);
  1081.      if (auto_preview_mode && ah->flag & A_READ && prompt_line >= 0) {
  1082.          how = ON_READ;
  1083.          mark();
  1084.      }
  1085.      next_cura = ++cura;
  1086.      
  1087.      switch (menu_cmd) {
  1088.          
  1089.       case MC_DO_KILL:
  1090.          if (!do_auto_kill()) break;
  1091.          elim_list[0] = firsta;
  1092.          elim_list[1] = firsta + cura;
  1093.          elim_articles(elim_list, 2);
  1094.          firsta = elim_list[0];
  1095.          next_cura = elim_list[1] - firsta;
  1096.          goto redraw;
  1097.          
  1098.       case MC_DO_SELECT:
  1099.          if (prompt_line >= 0) { /* not redrawn */
  1100.          do_auto_select((regexp *)NULL, 2);
  1101.          break;
  1102.          }
  1103.          numa = -1;
  1104.          do_auto_select((regexp *)NULL, 2);
  1105.          /* FALL THRU */
  1106.  
  1107.       case MC_QUIT:
  1108.          menu_return( ME_QUIT );
  1109.  
  1110.       case MC_NEXT:        /* article not found -- preview next */
  1111.          if (prompt_line == temp) 
  1112.          break; /* not redrawn -- return to menu instead */
  1113.          user_delay(1);
  1114.          /* FALL THRU */
  1115.          
  1116.       case MC_PREVIEW_NEXT:
  1117.          if (prompt_line < 0) {    /* redrawn screen ! */
  1118.          if ((firsta + cura) >= n_articles) goto redraw;
  1119.          if (auto_preview_mode &&
  1120.              (articles[firsta+cura]->flag & A_SAME) == 0) goto redraw;
  1121.          prompt_line = Lines;
  1122.          } else {
  1123.          if (ah->flag & (A_LEAVE | A_LEAVE_NEXT)) {
  1124.              cura--;
  1125.              how = ON_LEAVE;
  1126.              mark();
  1127.              cura++;
  1128.          }
  1129.          if (cura > numa) break;
  1130.          if (auto_preview_mode &&
  1131.              (articles[firsta+cura]->flag & A_SAME) == 0) break;
  1132.          prompt_line = temp;
  1133.          }
  1134.          article_id = cura;
  1135.          goto preview_next;
  1136.          
  1137.       case MC_PREVIEW_OTHER:
  1138.          prompt_line = temp;
  1139.          raw();
  1140.          goto preview_other;
  1141.          
  1142.       default:    
  1143.          if (prompt_line < 0) goto redraw;
  1144.          break;
  1145.      }
  1146.      
  1147.      prompt_line = temp;
  1148.      goto build_prompt;
  1149.      
  1150.       case K_LAYOUT:
  1151.      if (++fmt_linenum > 3) fmt_linenum = 0;
  1152.      goto redraw;
  1153.     
  1154.       default:
  1155.      msg("Command %d not supported", k_cmd);
  1156.      goto same_prompt;
  1157.      }
  1158.  
  1159.  
  1160.  menu_exit:
  1161.  
  1162.     firsta = o_firsta;
  1163.     in_menu_mode = o_mode;
  1164.     menu_level--;
  1165.     
  1166.     no_raw();
  1167.     return menu_cmd;
  1168. }
  1169.  
  1170.  
  1171. static show_articles()
  1172. {
  1173.     register article_number cur, next, temp;
  1174.     register article_header *ah;
  1175.     article_number elim_list[1];
  1176.     register int mode;
  1177.     int cmd, prev = -1, again;
  1178.     
  1179.     do {
  1180.     again = 0;
  1181.     
  1182.     for (cur = 0; cur < n_articles; cur++) {
  1183.         if (articles[cur]->flag & A_SELECT) break;
  1184.     }
  1185.     
  1186.     while (cur < n_articles) {
  1187.         
  1188.         for (next = cur+1; next < n_articles; next++) {
  1189.         if (articles[next]->flag & A_SELECT) break;
  1190.         }
  1191.         
  1192.         articles[cur]->flag &= ~(A_SELECT | A_AUTO);
  1193.         
  1194.      show:
  1195.         mode = 0;
  1196.         if (prev >= 0) mode |= MM_PREVIOUS;
  1197.         if (next == n_articles) mode |= MM_LAST_SELECTED;
  1198.         if ((cur + 1) >= n_articles) mode |= MM_LAST_ARTICLE;
  1199.         if (cur == 0) mode |= MM_FIRST_ARTICLE;
  1200.         
  1201.         cmd = more(ah = articles[cur], mode, 0);
  1202.         
  1203.         switch(cmd) {
  1204.          
  1205.          case MC_DO_KILL:
  1206.         if (do_auto_kill()) {
  1207.             elim_list[0] = next;
  1208.             elim_articles(elim_list, 1);
  1209.             cur = elim_list[0];
  1210.             if (cur < 0) cur = n_articles;
  1211.             continue;
  1212.         }
  1213.         break;
  1214.         
  1215.          case MC_DO_SELECT:
  1216.         for (temp = cur+1, cur = next; temp < n_articles; temp++) {
  1217.             if (auto_select_article(ah = articles[temp], 2)) {
  1218.             ah->flag |= A_SELECT;
  1219.             if (temp < cur) cur = temp;
  1220.             }
  1221.         }
  1222.         continue;
  1223.         
  1224.          case MC_PREV:
  1225.         if (prev == next) break;
  1226.         
  1227.         next = cur; cur = prev; prev = next;
  1228.         goto show;
  1229.         
  1230.          case MC_NEXTSUBJ:
  1231.         ah->flag |= A_READ;
  1232.         for (next = cur+1; next < n_articles; next++) {
  1233.             if (((ah = articles[next])->flag & A_SAME) == 0) break;
  1234.             ah->flag &= ~(A_SELECT | A_AUTO | A_LEAVE | A_LEAVE_NEXT);
  1235.         }
  1236.         for (; next < n_articles; next++) {
  1237.             if (articles[next]->flag & A_SELECT) break;
  1238.         }
  1239.         break;
  1240.         
  1241.          case MC_ALLSUBJ:
  1242.         for (next = cur+1; next < n_articles; next++) {
  1243.             ah = articles[next];
  1244.             if ((ah->flag & A_SAME) == 0) break;
  1245.             ah->flag &= ~(A_AUTO | A_LEAVE | A_LEAVE_NEXT | A_READ);
  1246.             ah->flag |= A_SELECT;
  1247.         }
  1248.         for (next = cur+1; next < n_articles; next++)
  1249.             if (articles[next]->flag & A_SELECT) break;
  1250.         break;
  1251.         
  1252.          case MC_MENU:
  1253.         ah->flag |= A_SELECT;
  1254.         firsta = cur - 5;
  1255.         if (firsta < 0) firsta = 0;
  1256.         next_cura = cur - firsta;
  1257.         
  1258.         return SH_MENU;
  1259.         
  1260.          case MC_QUIT:
  1261.         ah->flag |= A_SELECT;
  1262.         return SH_QUIT;
  1263.         
  1264.          case MC_NEXT:
  1265.         if ((ah->flag & (A_LEAVE | A_LEAVE_NEXT)) == 0)
  1266.             ah->flag |= A_READ;
  1267.         break;
  1268.  
  1269.          case MC_BACK_ART:
  1270.         if (cur > 0) {
  1271.             next = cur - 1;
  1272.             break;
  1273.         }
  1274.         goto show;
  1275.         
  1276.          case MC_FORW_ART:
  1277.         next = cur + 1;
  1278.         break;
  1279.         
  1280.          case MC_NEXTGROUP:
  1281.         ah->flag |= A_SELECT;
  1282.         return SH_NEXT;
  1283.         
  1284.          case MC_READGROUP:
  1285.         return SH_READ;
  1286.         }
  1287.         
  1288.         prev = cur; cur = next;
  1289.     }
  1290.  
  1291.     for (cur = 0; cur < n_articles; cur++) 
  1292.         if (articles[cur]->flag & A_SELECT) continue;
  1293.  
  1294.     for (cur = 0; cur < n_articles; cur++) {
  1295.         ah = articles[cur];
  1296.         if (ah->flag & A_LEAVE) {
  1297.         ah->flag &= ~A_LEAVE;
  1298.         ah->flag |= A_SELECT;
  1299.         again++;
  1300.         }
  1301.     }
  1302.  
  1303.     if (again > 1) {
  1304.         delayed_msg = "Showing %ld articles again";
  1305.         dl_msg_arg = again;
  1306.     } else
  1307.         if (again == 1)
  1308.         delayed_msg = "Showing article again";
  1309.     
  1310.     } while (again);
  1311.     
  1312.     return prev < 0 ? SH_NO_SELECT : SH_READ;
  1313. }
  1314.  
  1315. /*
  1316.  *    return article header for article on menu
  1317.  */
  1318.  
  1319. article_header *get_menu_article()
  1320. {
  1321.     register article_header *ah;
  1322.     
  1323.     fputs(" from article: ", stdout); fl;
  1324.     
  1325.     if (get_k_cmd() == K_ARTICLE_ID) {
  1326.     ah = articles[firsta + article_id];
  1327.     if (ah->a_group) init_group(ah->a_group);
  1328.     return ah;
  1329.     }
  1330.     
  1331.     return NULL;
  1332. }
  1333.  
  1334.  
  1335. static get_purpose(purpose)
  1336. char *purpose;
  1337. {
  1338. #ifdef NNTP
  1339.     return;            /* newsgroups file is not available */
  1340. #else    
  1341.     FILE *f;
  1342.     char line[256], group[80];
  1343.     register char *cp, *pp;
  1344.     register int len;
  1345.     extern char news_active[];
  1346.  
  1347.     strcpy(line, news_active);
  1348.     if ((cp = strrchr(line, '/')) == NULL) return;
  1349.     strcat(cp + 1, "newsgroups");
  1350.  
  1351.     if ((f = fopen(line, "r")) == NULL) return;
  1352.  
  1353.     sprintf(group, "%s\t", current_group->group_name);
  1354.     len = current_group->group_name_length + 1;
  1355.     
  1356.     while (fgets(line, 256, f) != NULL) {
  1357.     if (strncmp(line, group, len)) continue;
  1358.     cp = line + len;
  1359.     while (*cp && isspace(*cp)) cp++;
  1360.     for (pp = purpose, len = 76; --len >= 0 && *cp && *cp != NL; )
  1361.         *pp++ = *cp++;
  1362.     *pp = NUL;
  1363.     }
  1364.  
  1365.     fclose(f);
  1366. #endif
  1367. }
  1368.  
  1369. /*
  1370.  *    perform auto selections that are not already selected
  1371.  *    if article is in range firsta..firsta+numa (incl) mark article
  1372.  */
  1373.  
  1374. static do_auto_select(re, mode)
  1375. regexp *re;
  1376. int mode;
  1377. {
  1378.     register article_number i;
  1379.     register article_header *ah, **ahp;
  1380.     int count = 0, o_cura;
  1381.     
  1382.     o_cura = cura;
  1383.     
  1384.     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  1385.     ah = *ahp;
  1386.     if (re != NULL) {
  1387.         if (!regexec(re, ah->subject)) continue;
  1388.     } else
  1389.         if (!auto_select_article(ah, mode)) continue;
  1390.     
  1391.     count++;
  1392.     if (ah->flag & A_SELECT) continue;
  1393.     if (firsta <= i && i <= (firsta+numa)) {
  1394.         cura = i - firsta;
  1395.         how = ON;
  1396.         mark();
  1397.     } else
  1398.         ah->flag |= A_SELECT;
  1399.     }
  1400.     
  1401.     msg(count == 0 ? "No selections" : "Selected %d article%s",
  1402.        count, count > 1 ? "s" : "");
  1403.     cura = o_cura;
  1404. }
  1405.  
  1406. static do_auto_kill()
  1407. {
  1408.     register article_number i;
  1409.     register article_header *ah, **ahp;
  1410.     int any = 0;
  1411.     
  1412.     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  1413.     ah = *ahp;
  1414.     if (auto_select_article(ah, 0)) {
  1415.         ah->flag |= A_KILL;
  1416.         ah->flag &= ~(A_SELECT | A_AUTO);
  1417.         any = 1;
  1418.     }
  1419.     }
  1420.     return any;
  1421. }
  1422.  
  1423.         
  1424.  
  1425. /*
  1426.  *    read command from command line
  1427.  */
  1428.  
  1429. alt_command()
  1430. {
  1431.     int ok_val, macro_cmd;
  1432.     char *cmd, brkchars[10];
  1433.     extern char erase_key;
  1434.     extern int get_from_macro;
  1435.     extern int alt_completion();
  1436.  
  1437.     if (get_from_macro)
  1438.     ok_val = AC_UNCHANGED;
  1439.     else {
  1440.     prompt(":");
  1441.     ok_val = AC_PROMPT;
  1442.     }
  1443.     
  1444.  again:
  1445.     
  1446.     sprintf(brkchars, "?%c ", erase_key);
  1447.     
  1448.     cmd = get_s(NONE, NONE, brkchars, alt_completion);
  1449.     if (cmd == NULL ||
  1450.     *cmd == NUL || *cmd == SP || *cmd == erase_key)
  1451.     return ok_val;
  1452.  
  1453.     macro_cmd = get_from_macro;
  1454.     
  1455.     if (*cmd == '?') {
  1456.     display_file("help.extended", CLEAR_DISPLAY);
  1457.     ok_val = AC_REDRAW;
  1458.     goto new_prompt;
  1459.     }
  1460.  
  1461.     ok_val = parse_command(cmd, ok_val, (FILE *)NULL);
  1462.     if (ok_val != AC_REDRAW || !delay_redraw) return ok_val;
  1463.     
  1464.  new_prompt:
  1465.     if (macro_cmd) return ok_val;
  1466.     
  1467.     prompt_line = -1;
  1468.     printf("\n\r:");
  1469.     fl;
  1470.     goto again;
  1471. }
  1472.